package com.gwtintegration.smartrf.builder;

import java.util.Date;

import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JParameterizedType;
import com.google.gwt.user.rebind.SourceWriter;

public abstract class FieldConverterBuilder {

	private final JMethod getterMethod;
	private final JMethod setterMethod;
	private final ProxyConverterBuilder proxyConverterBuilder;

	private String attributeName;
	private String variableName;

	protected FieldConverterBuilder(JMethod getterMethod, ProxyConverterBuilder proxyConverterBuilder) {
		this.getterMethod = getterMethod;
		this.proxyConverterBuilder = proxyConverterBuilder;
		this.attributeName = JMethodUtils.getAttributeName(getterMethod);
		this.variableName = JMethodUtils.getVariableName(getterMethod);
		this.setterMethod = JMethodUtils.getSetter(getterMethod);
	}

	public abstract boolean buildToProxy(SourceWriter sourceWriter) throws UnableToCompleteException;

	public abstract boolean buildToRecord(SourceWriter sourceWriter) throws UnableToCompleteException;

	public boolean buildEquals(SourceWriter sourceWriter) throws UnableToCompleteException {
		return false;
	}

	protected void extractPrimitiveRecordAttribute(SourceWriter sourceWriter, String varName, String attrName, JClassType returnType) {
		if (String.class.getName().equals(returnType.getQualifiedSourceName())) {
			sourceWriter.println("String %s = record.getAttribute(\"%s\");", varName, attrName);
		} else if (Integer.class.getName().equals(returnType.getQualifiedSourceName())) {
			sourceWriter.println("Integer %s = record.getAttributeAsInt(\"%s\");", varName, attrName);
		} else if (Long.class.getName().equals(returnType.getQualifiedSourceName())) {
			sourceWriter.println("Long %s = record.getAttributeAsLong(\"%s\");", varName, attrName);
		} else if (Float.class.getName().equals(returnType.getQualifiedSourceName())) {
			sourceWriter.println("Float %s = record.getAttributeAsFloat(\"%s\");", varName, attrName);
		} else if (Double.class.getName().equals(returnType.getQualifiedSourceName())) {
			sourceWriter.println("Double %s = record.getAttributeAsDouble(\"%s\");", varName, attrName);
		} else if (Date.class.getName().equals(returnType.getQualifiedSourceName())) {
			sourceWriter.println("Date %s = record.getAttributeAsDate(\"%s\");", varName, attrName);
		}
	}

	public JMethod getGetterMethod() {
		return getterMethod;
	}

	public JMethod getSetterMethod() {
		return setterMethod;
	}

	public boolean hasSetterMethod() {
		return setterMethod != null;
	}

	public ProxyConverterBuilder getProxyConverterBuilder() {
		return proxyConverterBuilder;
	}

	public String getAttributeName() {
		return attributeName;
	}

	public String getVariableName() {
		return variableName;
	}

	public static FieldConverterBuilder create(JMethod method, ProxyConverterBuilder proxyConverterBuilder) throws UnableToCompleteException {

		JClassType returnType = method.getReturnType().isClassOrInterface();

		if (JMethodUtils.isPrimaryKey(method)) {

			if (JTypeUtils.isPrimitive(returnType)) {

				return new PrimitivePrimaryKeyFieldConverterBuilder(method, proxyConverterBuilder);

			} else if (returnType.isAssignableTo(returnType)) {

				return new CompositePrimaryKeyFieldConverterBuilder(method, proxyConverterBuilder);

			} else {
				proxyConverterBuilder.getLogger().log(Type.ERROR,
						"Invalid primary key field type inside proxy " + returnType.getQualifiedSourceName() + ".");
				throw new UnableToCompleteException();
			}
		} else if (JTypeUtils.isPrimitive(returnType)) {

			return new PrimitiveFieldConverterBuilder(method, proxyConverterBuilder);

		} else if (returnType.isInterface() != null && JTypeUtils.hasGenerateConverterAnnotation(returnType)) {

			return new ProxyFieldConverterBuilder(method, proxyConverterBuilder);

		} else if (JTypeUtils.isCollection(returnType)) {

			JParameterizedType parameterized = returnType.isParameterized();
			JClassType parameter = JTypeUtils.getSingleParameterType(parameterized);

			if (JTypeUtils.isPrimitive(parameter)) {

				if (!Integer.class.getName().equals(parameter.getQualifiedSourceName())
						&& !String.class.getName().equals(parameter.getQualifiedSourceName())) {
					proxyConverterBuilder.getLogger().log(Type.ERROR,
							"Can't convert to record a collection of " + parameter.getName() + ", only Integer and Strings are allowed.");
					throw new UnableToCompleteException();
				}

				return new PrimitiveCollectionFieldConverterBuilder(method, proxyConverterBuilder);

			} else if (JTypeUtils.hasGenerateConverterAnnotation(parameter)) {

				return new ProxyCollectionFieldConverterBuilder(method, proxyConverterBuilder);

			} else {
				proxyConverterBuilder.getLogger().log(Type.WARN, "The Collection of type " + parameterized.getName() + " is not supported.");
			}
		}

		return null;
	}
}
